package com.javacoo.xservice.base.support.lock.internal;

import java.util.concurrent.TimeUnit;

import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.Assert;

import com.javacoo.xservice.base.support.lock.Lock;

import lombok.extern.slf4j.Slf4j;

/**
 * 锁接口实现类
 * <li>基于Redssion</li>
 *
 * @author: duanyong@jccfc.com
 * @since: 2020/6/22 10:39
 */
@Slf4j
public class RedssionLock implements Lock<RLock> {
    /**约定缓存前缀：appId:模块:key*/
    public static final String CACHE_PREFIX_KEY ="javacoo:service:lock:";
    @Autowired
    private RedissonClient redissonClient;
    /**
     * 对lockKey加锁
     * <li>阻塞获取锁</li>
     *
     * @param lockKey :lockKey
     * @author duanyong@jccfc.com
     * @date 2020/6/22 10:30
     * @return: T 锁对象
     */
    @Override
    public RLock lock(String lockKey) {
        log.info(">>>>> lock key[{}]",lockKey);
        Assert.hasText(lockKey,"lockKey 不能为空");
        RLock lock = redissonClient.getLock(CACHE_PREFIX_KEY +lockKey);
        lock.lock();
        return lock;
    }

    /**
     * 对lockKey加锁,timeout后过期
     * <li></li>
     *
     * @param lockKey : lockKey
     * @param timeout : 锁超时时间，单位：秒
     * @author duanyong@jccfc.com
     * @date 2020/6/22 10:31
     * @return: T 锁对象
     */
    @Override
    public RLock lock(String lockKey, int timeout) {
        return lock(lockKey, TimeUnit.SECONDS,timeout);
    }

    /**
     * 对lockKey加锁,指定时间单位，timeout后过期
     * <li></li>
     *
     * @param lockKey : lockKey
     * @param unit : 时间单位
     * @param timeout : 锁超时时间
     * @author duanyong@jccfc.com
     * @date 2020/6/22 10:33
     * @return: T 锁对象
     */
    @Override
    public RLock lock(String lockKey, TimeUnit unit, int timeout) {
        log.info(">>>>> lockKey:{},TimeUnit:{},timeout:{}",lockKey,unit,timeout);
        Assert.hasText(lockKey,"lockKey 不能为空");
        RLock lock = redissonClient.getLock(CACHE_PREFIX_KEY +lockKey);
        lock.lock(timeout, unit);
        return lock;
    }

    /**
     * 尝试获取锁
     * <li>获取锁失败立即返回，上锁后60秒自动释放锁</li>
     *
     * @param lockKey : lockKey
     * @author duanyong@jccfc.com
     * @date 2020/6/22 10:35
     * @return: boolean 是否成功,成功返回:true
     */
    @Override
    public boolean tryLock(String lockKey) {
        return tryLock(lockKey,0,60);
    }

    /**
     * 尝试获取锁
     * <li></li>
     *
     * @param lockKey : lockKey
     * @param waitTime :最多等待时间
     * @param timeout :上锁后自动释放锁时间
     * @author duanyong@jccfc.com
     * @date 2020/6/22 10:35
     * @return: boolean 是否成功,成功返回:true
     */
    @Override
    public boolean tryLock(String lockKey, int waitTime, int timeout) {
        return tryLock(lockKey, TimeUnit.SECONDS,waitTime,timeout);
    }

    /**
     * 尝试获取锁
     * <li></li>
     *
     * @param lockKey : lockKey
     * @param unit :时间单位
     * @param waitTime :最多等待时间
     * @param timeout :上锁后自动释放锁时间
     * @author duanyong@jccfc.com
     * @date 2020/6/22 10:36
     * @return: boolean 是否成功,成功返回:true
     */
    @Override
    public boolean tryLock(String lockKey, TimeUnit unit, int waitTime, int timeout) {
        Assert.hasText(lockKey,"lockKey 不能为空");
        lockKey = CACHE_PREFIX_KEY +lockKey;
        log.info(">>>>> tryLock lockKey:{},TimeUnit:{},waitTime:{},timeout:{}",lockKey,unit,waitTime,timeout);
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, timeout, unit);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("尝试获取锁:{}，TimeUnit:{},waitTime:{},timeout:{},失败",lockKey,unit,waitTime,timeout,e);
        }
        return false;
    }

    /**
     * 释放锁
     * <li></li>
     *
     * @param lockKey : lockKey
     * @author duanyong@jccfc.com
     * @date 2020/6/22 10:37
     * @return: void
     */
    @Override
    public void unlock(String lockKey) {
        lockKey = CACHE_PREFIX_KEY +lockKey;
        try {
            RLock lock = redissonClient.getLock(lockKey);
            if(!lock.isLocked()){
                log.error(">>>>> unlock lockKey fail:{},isLocked:{}",lockKey,lock.isLocked());
                return;
            }
            if(!lock.isHeldByCurrentThread()){
                log.error(">>>>> unlock lockKey fail:{},isHeldByCurrentThread:{}",lockKey,lock.isHeldByCurrentThread());
                return;
            }
            lock.unlock();
            log.info(">>>>> unlock lockKey success:{}",lockKey);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("释放锁失败:{}",lockKey,e);
        }
    }

    /**
     * 释放锁
     * <li></li>
     *
     * @param lock :锁对象
     * @author duanyong@jccfc.com
     * @date 2020/6/22 10:37
     * @return: void
     */
    @Override
    public void unlock(RLock lock) {
        lock.unlock();
    }
}
